#include "connector.h"

Connector::Connector(const char* host, const char* port)
:m_host(host),m_port(port)
{
  create_socket();
}

Connector::~Connector()
{
  char buff[128];
  memset(buff,0,sizeof(buff));  
  shutdown(m_sockfd,SHUT_WR);
  while (read(m_sockfd,buff,sizeof(buff)) > 0) 
  {
    ;
  }
  close(m_sockfd);
}

int Connector::connect_host(int timeout)
{
  int flags,error,ret;
  socklen_t err_len;
  fd_set rset,wset;
  struct timeval tval;

  flags = fcntl(m_sockfd,F_GETFL,0);
  fcntl(m_sockfd,F_SETFL, flags | O_NONBLOCK);
  
  error = 0;
  int optval = 1;
  setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
  ret = connect(m_sockfd,(const struct sockaddr*)(&m_addr),sizeof(m_addr));

  if (ret < 0)
  {
    if (errno != EINPROGRESS)
    {
      printf("connect error:%s\n",strerror(errno));
      return -1;
    }
  }
    
  if (ret == 0)
  {
    goto done;
  }
 
  printf("start to selecting...\n");
  FD_ZERO(&rset);
  FD_SET(m_sockfd,&rset);
  wset = rset;
  tval.tv_sec = timeout;
  tval.tv_usec = 0;

  ret = select(m_sockfd+1,&rset,&wset,NULL,timeout ? &tval : NULL);
  if (ret == 0)
  {
    close(m_sockfd);
    errno = ETIMEDOUT;
    printf("select timeout:%s\n",strerror(errno));
    return -1;
  }
 
  if (FD_ISSET(m_sockfd,&rset) || FD_ISSET(m_sockfd,&wset))
  {
    err_len = sizeof(error);
    
    // use getpeername to make sure the connection is established
    struct sockaddr_in svraddr;
    socklen_t svr_len;
    svr_len = sizeof(svraddr);
    int ret = getpeername(m_sockfd,(struct sockaddr*)(&svraddr),&svr_len);
    if (ret == -1)
    {
      if (errno == ENOTCONN)
      {
        if (getsockopt(m_sockfd,SOL_SOCKET,SO_ERROR,&error,&err_len) < 0)
        {
          printf("getsockopt error:%s\n",strerror(errno));
          return -1;
        }    
      }
      else
      {
	printf("getpeername error:%s.\n",strerror(errno));
      }
    } // end of ret == -1
    else
    {
      char buff[128] = {'\0'};
      printf("connection established.\n");
      printf("The server is : %s:%d\n",inet_ntop(AF_INET,&svraddr.sin_addr,buff, sizeof(buff)),ntohs(svraddr.sin_port));
    }
  }
  else
  {
    printf("select error:%s\n",strerror(errno));
    exit(-1);
  }

  done:
    fcntl (m_sockfd,F_SETFL,flags);
    if (error)
    {
      close(m_sockfd);
      errno = error;
      printf("connect done error:%s\n",strerror(errno));
      return -1;
    }
  
    return 0;
}

int Connector::bind_host()
{
  int optval = 1;
  setsockopt(m_sockfd,SOL_SOCKET,SO_REUSEPORT,&optval,sizeof(optval));
  int ret = bind(m_sockfd,(const struct sockaddr*)(&m_addr),sizeof(m_addr));    
  if (ret < 0)
  {
    printf("\nbind %s:%s error:%s\n",m_host,m_port,strerror(errno));
    return -1;
  }
  listen(m_sockfd,5); 

  return 0;
}

int Connector::accept_host()
{
  time_t ticks;
  char buff[128] = {'\0'};
  socklen_t len = sizeof(m_cliaddr);
    
 
  for(;;)
  {
    m_connfd = accept(m_sockfd,(struct sockaddr*)(&m_cliaddr),&len);
    if (m_connfd < 0)
    {
      printf("accpet error:%s\n",strerror(errno));
      return -1;
    }
    else
    {
      printf("accpeting...\n");
      printf("connected from : %s:%d\n",inet_ntop(AF_INET,&m_cliaddr.sin_addr,buff,
      sizeof(buff)),ntohs(m_cliaddr.sin_port));
    }

    memset(buff,0,sizeof(buff));
    ticks = time(NULL);
    snprintf(buff,sizeof(buff),"%.25s\r\n",ctime(&ticks));
    write(m_connfd,buff,sizeof(buff));
    close(m_connfd);
  }

  return 0;
}

int Connector::accept_select_host()
{
  int maxi, maxfd,connfd,sockfd,i;
  int nready,client[FD_SETSIZE];
  ssize_t n;
  fd_set rset,allset;
  char buf[128] = {'\0'};
  socklen_t clilen;  
  
  maxfd = m_sockfd;
  maxi = -1;
  for (int i = 0; i < FD_SETSIZE; i++)
  {
    client[i] = -1;
  } 
  FD_ZERO(&allset);
  FD_SET(m_sockfd,&allset);
  
  for(;;)
  {
    rset = allset;
    nready = select(maxfd+1,&rset,NULL,NULL,NULL);
    
    if (FD_ISSET(m_sockfd,&rset))
    {
      printf("listening socket readable.\n");
      sleep(5);
      clilen = sizeof(m_cliaddr);      
      connfd = accept(m_sockfd,(struct sockaddr*)(&m_cliaddr),&clilen);
      
      if (connfd > 0) 
      {
        char buf[128] = {'\0'};
        printf("accept success from client :%s:%d\n",inet_ntop(AF_INET,&m_cliaddr.sin_addr,buf,sizeof(buf)),
        ntohs(m_cliaddr.sin_port));
      }
      else
      {
	printf("accept error:%s\n",strerror(errno));
      }
      // init client array

      for (i = 0; i < FD_SETSIZE; i++)
      {
        if (client[i] < 0)
        {
	  client[i] = connfd;
	  break;
	}
      }

      if ( i == FD_SETSIZE)
      {
	printf("too many client.\n");
	return -1;
      }	

      // select for connfd
      FD_SET(connfd,&allset);
      if (connfd > maxfd) maxfd = connfd;
      if (i > maxi) maxi = i;

      if (--nready <= 0) continue;
    } // end of FD_ISSET
    
    // check for connected clients
    for (i = 0; i <= maxi; i++)
    {
      sockfd = client[i];
      if (sockfd < 0) continue;
      
      if (FD_ISSET(sockfd,&rset))
      {
	// for daytime   
        const char* msg = "I got your request";     
        write(sockfd,msg,strlen(msg));
        close(sockfd);
        FD_CLR(sockfd,&allset);
        client[i] = -1;        
      }
     
      if (--nready <= 0) break;
    } 
  }
}

int Connector::daytime()
{
  char buff[32] = {'\0'};
  int n;
  
  write(m_sockfd,"",1);

  while ( (n = read(m_sockfd,buff,sizeof(buff))) > 0)
  {
    buff[n] = 0;
    int ret = fputs(buff,stdout);
    if (ret == EOF)
    {
      printf("fputs error:%s\n",strerror(errno));
      return -1;
    }
  }

  return 0;
}

int Connector::rst_test()
{
  struct linger ling;
  
  connect(m_sockfd,(struct sockaddr*)(&m_addr),sizeof(m_addr));
  // this will send RST to the server
  ling.l_onoff = 1;
  ling.l_linger = 0;
  setsockopt(m_sockfd,SOL_SOCKET,SO_LINGER,&ling,sizeof(ling)); 
  close(m_sockfd); 
}

void Connector::create_socket()
{
  m_sockfd = socket(AF_INET,SOCK_STREAM,0);
  if (m_sockfd < 0)
  {
    printf("socket create error:%s\n",strerror(errno));
    exit(-1);
  }  

  memset(&m_addr,0,sizeof(m_addr));
  m_addr.sin_family = AF_INET;
  m_addr.sin_port = htons(atoi(m_port));
  int ret = inet_pton(AF_INET,m_host,&m_addr.sin_addr);
  if (ret < 0)
  {
    printf("inet_pton error:%s\n",strerror(errno));
    exit(-1);
  }
}
